package Games;

import java.util.ArrayList;
import java.util.Arrays;

class OthelloMove implements Comparable<OthelloMove>, Runnable {
	private int move;		// de move waar de gunstigheid van wordt berekend
	private int player;		// voor wie we de gunstigheid van de move berekenen
	private int[][] board;	// toestand speelbord bij aanmaken OthelloMove node
	private int[] flips;	// flips die deze move zou opleveren als hij wordt gedaan
	private int free;		// aantal vrije plekken dat er voor het doen van deze move nog is
	private int baseValue;	// waarde van deze move exclusief children
	private OthelloMove[] children;
	
	public static int depthFree;
	public static Object lock = new Object();

	boolean childrenFilled = false;
	boolean die = false;

	public OthelloMove(int move, int player, int[][] board, int[] flips, int free) {
		this.move = move;
		this.player = player;
		this.board = board;
		this.flips = flips;
		this.free = free;
		this.baseValue = 0;
		children = new OthelloMove[0];
		if (move==-1) {
			depthFree = free;
		}
		OthelloAI.pool.submit(this);
	}

	public void run() {
		if (die)
			return;

		int[][] newBoard = OthelloAI.copyBoard(board);

		if (move!=-1) {
			if (OthelloAI.isCorner(move))
				baseValue += OthelloAI.VALUE_CORNER;
			else if (OthelloAI.isXCorner(move, player, board))
				baseValue += OthelloAI.VALUE_X;
			else if (OthelloAI.isSide(move))
				baseValue += OthelloAI.VALUE_SIDE;
	
			int aantalFlips = OthelloAI.countFlips(flips);
			if (free < OthelloAI.DIMENSION*OthelloAI.DIMENSION/3)
				baseValue += aantalFlips * 2;
			else
				baseValue += aantalFlips / 2;
	
			OthelloAI.playMove(move, player, newBoard, flips);
		} else {
			player = OthelloAI.otherPlayer(player);
		}
		
		synchronized (lock) {
			while (depthFree-free > OthelloAI.DEPTH) {
				try {
					lock.wait();
				} catch (InterruptedException ie) {
					return;
				}
			}
		}

		ArrayList<OthelloMove> newMoves = new ArrayList<OthelloMove>();
		boolean weCanDoMove = false;
		int[] scores = new int[2];
		scores[0]=0;
		scores[1]=0;
		for (int newMove = 0; newMove < OthelloAI.DIMENSION*OthelloAI.DIMENSION; newMove++) {
			if (newBoard[OthelloAI.yFromMove(newMove)][OthelloAI.xFromMove(newMove)] != OthelloAI.EMPTY)
				scores[newBoard[OthelloAI.yFromMove(newMove)][OthelloAI.xFromMove(newMove)]]++;

			int[] localFlips = new int[OthelloAI.NW+1];
			if (!weCanDoMove && OthelloAI.moveOk(newMove, player, newBoard, localFlips))
				weCanDoMove = true;

			if (!OthelloAI.moveOk(newMove, OthelloAI.otherPlayer(player), newBoard, localFlips))
				continue;

			// elke mogelijke zet die de tegenstander krijgt is ongunstig.
			// ofwel hoe minder value verlaagd wordt, hoe beter deze zet.
			baseValue -= OthelloAI.VALUE_MOBILITY;

			newMoves.add(new OthelloMove(newMove, OthelloAI.otherPlayer(player), newBoard, localFlips, free-1));
		}
		children = newMoves.toArray(children);
//		System.out.println("Nieuwe lengte children: "+children.length+" bij vrij: "+free);
		if (die) {
			kill();
			return;
		}
		if (children.length > 0)
			childrenFilled=true;

		if (!weCanDoMove && children.length==0) {
			if (scores[player]>scores[OthelloAI.otherPlayer(player)])
				baseValue += OthelloAI.VALUE_WIN;
			else
				baseValue -= OthelloAI.VALUE_WIN;
		} else if (children.length==0) {
			baseValue += OthelloAI.VALUE_SKIP;
		}
	}

	public void sort() {
		if (!childrenFilled)
			return;
		for (int c = 0; c < children.length; c++)
			children[c].sort();
		Arrays.sort(children);
	}

	private int getValue() {
		if (childrenFilled)
			return baseValue-children[children.length-1].getValue();
		return baseValue;
	}

	public int getMove() {
		return move;
	}

	public OthelloMove chooseMove() {
		if (!childrenFilled)
			System.out.println("chooseMove() voordat children klaar zijn: "+(depthFree-free)+", ("+OthelloAI.xFromMove(move)+","+OthelloAI.yFromMove(move)+")");
		sort();
		for (int c = 0; c < children.length-1; c++) {
			children[c].kill();
		}
		return children[children.length-1];
	}

	public OthelloMove[] getChildren() {
		if (childrenFilled)
			return children;
		return new OthelloMove[0];
	}

	public void kill() {
		die = true;
		if (childrenFilled) {
			for (int c = 0; c < children.length; c++) {
				children[c].kill();
			}
		}
	}
	
	public int compareTo(OthelloMove om) {
		return this.getValue()-om.getValue();
	}
}